package cn.seaboot.commons.file;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;

import org.jetbrains.annotations.NotNull;

import cn.seaboot.commons.exception.RuntimeIOException;

/**
 * ZIP 文件写入工具
 *
 * @author Mr.css
 * @version 2025-04-23 17:01
 */
public class ZipWriter implements Closeable {

    private final ZipOutputStream zos;

    public ZipWriter(OutputStream os) {
        this.zos = new ZipOutputStream(os);
    }

    /**
     * 写入整个文件夹到指定路径
     * <p>
     * 写入后，文档结构与之前保持一致
     *
     * @param root 输入
     * @param path 根目录，为了避免出错，至少传个空串
     */
    public void writeDirectory(File root, @NotNull String path) {
        int length = root.getAbsolutePath().length();
        FileUtils.eachFile(root, file -> {
            try (InputStream is = Files.newInputStream(file.toPath())) {
                this.write(is, path + "/" + file.getPath().substring(length));
            } catch (IOException e) {
                throw new RuntimeIOException("can not write file into zip:" + file.getName(), e);
            }
        });
    }

    /**
     * 将文件写入到指定路径
     *
     * @param path 文件位置，需包含：文件路径、文件名、扩展名
     * @param file 输入
     * @throws IOException -
     */
    public void write(File file, String path) throws IOException {
        try (InputStream is = Files.newInputStream(file.toPath())) {
            this.write(is, path);
        }
    }

    /**
     * 将文件写入到指定路径
     *
     * @param path 文件位置，需包含：文件路径、文件名、扩展名
     * @param is   输入
     * @throws IOException -
     */
    public void write(InputStream is, String path) throws IOException {
        zos.putNextEntry(new ZipEntry(path));
        IOUtils.copy(is, zos);
        zos.closeEntry();
    }

    /**
     * 将文件写入到指定路径
     *
     * @param path 文件位置，需包含：文件路径、文件名、扩展名
     * @param data 文字
     * @throws IOException -
     */
    public void write(byte[] data, String path) throws IOException {
        zos.putNextEntry(new ZipEntry(path));
        zos.write(data);
        zos.closeEntry();
    }

    /**
     * 关闭压缩流，结束写入的时候调用一下
     * <p>
     * Finishes writing the contents of the ZIP output stream without closing
     * the underlying stream. Use this method when applying multiple filters
     * in succession to the same output stream.
     *
     * @throws ZipException if a ZIP file error has occurred
     * @throws IOException  if an I/O exception has occurred
     */
    public void finish() throws IOException {
        zos.finish();
    }

    /**
     * 关闭 IO 流
     *
     * @throws IOException -
     */
    @Override
    public void close() throws IOException {
        zos.close();
    }
}
